/**
 * Hex
 *
 * @author Chris Pratt
 * @version 1.0
 *
 * 11/30/1998
 */
package com.anodyzed.onyx.util;

import java.io.PrintWriter;
import java.io.StringWriter;

public class Hex {
  private static final char[] DECIMAL = {'0','1','2','3','4','5','6','7','8','9'};
  private static final char[] DIGITS = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

    /**
     * Constructor - Private to prevent instantiation
     */
  private Hex () {
  } //Hex

  /**
   * Return the decimal character for the integer value
   *
   * @param c The integer value
   * @return the character
   */
  public static char chr (int c) {
    return DECIMAL[c & 0xF];
  } //chr

  /**
   * Return the hexadecimal character for the integer value
   *
   * @param c The integer value
   * @return the character (upper-case)
   */
  public static char hex (int c) {
    return DIGITS[c & 0xF];
  } //hex

  /**
   * Return the ordinal number of the character provided
   *
   * @param c One of (0 - 9, A - F, a - f)
   * @return An integer between 0 and 15
   */
  public static int ord (char c) {
    if(c <= '9') {
      return (c) - ('0');
    } else if(c <= 'F') {
      return (c) - ('A') + 10;
    } else {
      return (c) - ('a') + 10;
    }
  } //ord

  /**
   * Convert an array of bytes to an (upper-case) string of hexadecimal characters
   *
   * @param bytes The array of bytes
   * @return the printable string
   */
  public static String toString (byte[] bytes) {
    StringBuilder buf = new StringBuilder(bytes.length * 2);
    for(byte b : bytes) {
      buf.append(DIGITS[(b & 0xF0) >> 4]).append(DIGITS[b & 0x0F]);
    }
    return buf.toString();
  } //toString

  /**
   * Parse a string of hexadecimal characters into an array of bytes
   *
   * @param str The string to be parsed
   * @return an array of bytes
   */
  public static byte[] parseBytes (String str) {
    int pos = 0,count = str.length() / 2;
    byte[] bytes = new byte[count];
    for(int i = 0;i < count;i++) {
      bytes[i] = (byte)Integer.parseInt(str.substring(pos++,pos++),16);
    }
    return bytes;
  } //parseBytes

  /**
   * Return a string representation of the offset right padded with 0's
   * suitable for printing
   *
   * @param num the Offset Index
   * @return The Offset String
   */
  private static String offset (int num) {
    char[] buf = new char[6];
    buf[0] = chr(num / 100000);
    num %= 100000;
    buf[1] = chr(num / 10000);
    num %= 10000;
    buf[2] = chr(num / 1000);
    num %= 1000;
    buf[3] = chr(num / 100);
    num %= 100;
    buf[4] = chr(num / 10);
    buf[5] = chr(num % 10);
    return new String(buf);
  } //offset

  /**
   * Create a Hex dump
   *
   * @param buf The Buffer of Bytes to be output
   * @param off The Offset from the beginning of the buffer
   * @param len The Number of bytes to be written
   * @return A String containing the Hex Dump
   */
  public static String dump (byte[] buf,int off,int len) {
    StringWriter str = new StringWriter();
    PrintWriter out = new PrintWriter(str);
    int i;
    int pos = off;
    int count,size = len;

    while(size > 0) {
      out.print(offset(pos) + ": ");
      count = (size >= 16) ? 16 : size;
      for(i = 0;i < count;i++) {
        if(i == 8) {
          out.print(' ');
        }
        out.print(hex(buf[i + pos] >>> 4));
        out.print(hex(buf[i + pos]));
        out.print(' ');
      }
      if(i < 16) {
        if(i <= 8) {
          out.print(' ');
        }
        while(i++ < 16) {
          out.print("   ");
        }
      }
      out.print(' ');
      for(i = 0;i < count;i++) {
        if(i == 8) {
          out.print(' ');
        }
        if((buf[i + pos] >= 32) && (buf[i] < 128)) {
          out.print((char)buf[i + pos]);
        } else {
          out.print('.');
        }
      }
      out.println();
      pos += count;
      size -= count;
    }
    return str.toString();
  } //dump

  /**
   * Create a hex dump
   *
   * @param buf The buffer of bytes to be output
   * @return A String containing the Hex Dump
   */
  public static String dump (byte[] buf) {
    return dump(buf,0,buf.length);
  } //dump

  /**
   * Create a hex dump
   *
   * @param buf The string of bytes to be output
   * @return A String containing the Hex Dump
   */
  public static String dump (String buf) {
    return dump(buf.getBytes());
  } //dump

  /**
   * Create a hex dump
   *
   * @param buf an array of characters
   * @return a string containing the Hex Dump
   */
  public static String dump (char[] buf) {
    return dump(new String(buf));
  } //dump

  /**
   * Create a hex dump
   *
   * @param buf an array of characters
   * @param ofs the starting offset in the array
   * @param len the number of characters to dump
   * @return a string containing the Hex Dump
   */
  public static String dump (char[] buf,int ofs,int len) {
    return dump(new String(buf,ofs,len));
  } //dump

  /**
   * Hex Dump an Object
   *
   * @param obj The object to be dumped
   * @return The Hex Dump of the object
   */
  public static String dump (Object obj) {
    return dump(obj.toString());
  } //dump

} //*Hex
